#include <iostream>
#include <pthread.h>

#include <sys/time.h>
#include <unistd.h>


pthread_mutex_t a_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t got_request1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t got_request2 = PTHREAD_COND_INITIALIZER;

int g_data;

void*
functOne(void *data)
{
    pthread_mutex_t l_mutex = PTHREAD_MUTEX_INITIALIZER;

    int t_data = *(int*)data;
    std::cout << "In thread one " << t_data << std::endl;
    for(int i(0);i<30;i++)
    {
        if(2 == i)
        {
            std::cout << "One try to lock mutex\n ";
            pthread_mutex_lock(&a_mutex);
            std::cout << "One locked mutex " << g_data << std::endl;
            usleep(50000);
            g_data = 1;
            pthread_mutex_unlock(&a_mutex);
            std::cout << "One unlocked mutex " << g_data << std::endl;
        }
        if(5 == i)
        {
            std::cout << "Thread one condintional locked\n";
            pthread_mutex_lock(&l_mutex);
            pthread_cond_wait(&got_request1,&l_mutex);
            pthread_mutex_unlock(&l_mutex);
            std::cout << "Thread one condintional unlocked\n";
        }
        if(10 == i)
        {
            struct timeval  now;    
            struct timespec timeout; 
            
            gettimeofday(&now,NULL);
            timeout.tv_sec = now.tv_sec + 1;
            timeout.tv_nsec = now.tv_usec * 1000;

            std::cout << "Thread one time lock\n";
            pthread_mutex_lock(&l_mutex);
            int rc = pthread_cond_timedwait(&got_request1,&l_mutex,&timeout);
            if(rc)
            {
                //if(errorno == ETIMEDOUT)
                { 
                    std::cout << "Thread one unlocked by time\n";
                }
            }
            else
            {
                std::cout << "Thread one unlocked by main\n";
            }
            pthread_mutex_unlock(&l_mutex);
        }
        for (int j=0; j<500000; j++); /* delay loop */
        std::cout << "Thread one " << i << std::endl;
    }
    pthread_exit(NULL);
}


void*
functTwo(void *data)
{
    pthread_mutex_t l_mutex = PTHREAD_MUTEX_INITIALIZER;

    int t_data = *(int*)data;
    std::cout << "In thread one " << t_data << std::endl;
    for(int i(0);i<30;i++)
    {
        if(2 == i)
        {
            std::cout << "Two try to lock mutex\n ";
            pthread_mutex_lock(&a_mutex);
            std::cout << "Two locked mutex " << g_data << std::endl;
            usleep(50000);
            g_data = 2;
            pthread_mutex_unlock(&a_mutex);
            std::cout << "Two unlocked mutex " << g_data << std::endl;
        }
        if(5 == i)
        {
            std::cout << "Thread two condintional lock\n";
            pthread_mutex_lock(&l_mutex);
            pthread_cond_wait(&got_request2,&l_mutex);
            pthread_mutex_unlock(&l_mutex);
            std::cout << "Thread two condintional unlocked\n";
        }
        if(10 == i)
        {
            struct timeval  now;    
            struct timespec timeout; 
            
            gettimeofday(&now,NULL);
            timeout.tv_sec = now.tv_sec;// + 2;
            timeout.tv_nsec = (now.tv_usec+900) * 1000;

            std::cout << "Thread two time lock\n";
            pthread_mutex_lock(&l_mutex);
            int rc = pthread_cond_timedwait(&got_request2,&l_mutex,&timeout);
            if(rc)
            {
                //if(errorno == ETIMEDOUT)
                { 
                    std::cout << "Thread two unlocked by time\n";
                }
            }
            else
            {
                std::cout << "Thread two unlocked by main\n";
            }
            pthread_mutex_unlock(&l_mutex);
        }
        for (int j=0; j<500000; j++); /* delay loop */
        std::cout << "Thread two " << i << std::endl;
    }
    pthread_exit(NULL);
}

int main()
{
    int thread_id_one(9);
    int thread_id_two(9);
    pthread_t thread_one;
    pthread_t thread_two;

    int data_one(1);
    int data_two(2);

    std::cout << "begin program\n";

    thread_id_one = pthread_create(&thread_one,NULL,functOne,(void*)&data_one);
    std::cout << "created thread one " << thread_id_one << std::endl;

    thread_id_two = pthread_create(&thread_two,NULL,functTwo,(void*)&data_two);
    std::cout << "created thread two " << thread_id_two << std::endl;

    for(int i(0);i<40;i++)
    {
        if(10 == i)
        {
           std::cout << "Main unconditional\n";
           pthread_cond_signal(&got_request1); 
           pthread_cond_signal(&got_request2); 
        }
        if(20 == i)
        {
           std::cout << "Main unlock 2\n";
           pthread_cond_signal(&got_request2); 
           usleep(1000000);
        }
        if(30 == i)
        {
           std::cout << "Main unlock 1\n";
           pthread_cond_signal(&got_request1); 
        }
        for (int j=0; j<500000; j++); /* delay loop */
        std::cout << "Main " << i << std::endl;
    }
    usleep(100000);

    return 0;
}
